哈囉,大家好!經過前面的努力,我們已經建立了個人財務管理系統的基本功能。現在,是時候來確保我們的程式碼品質了。今天,我想和大家聊聊單元測試,以及它為什麼不僅僅是為了找出錯誤,更是提升我們開發技能的重要一環。
還記得有一次,我在專案中修改了一個小小的函式,結果導致其他功能無法正常運作。那時候才深刻體會到單元測試的重要性。單元測試不僅能幫助我們找到潛在的錯誤,更能:
Laravel 內建了強大的測試框架,使用 PHPUnit 進行測試。我們可以輕鬆地為應用程式撰寫和執行測試。
為了不影響開發資料庫,我們需要為測試環境建立一個獨立的資料庫。
使用 Artisan 指令建立測試類別:
php artisan make:test Models/UserTest --unit
這會在 tests/Unit/Models 目錄下建立 UserTest.php。
在 UserTest.php 中,我們可以開始撰寫測試。
<?php
namespace Tests\Unit\Models;
use Tests\TestCase;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function it_can_create_a_user()
{
$user = User::factory()->create([
'username' => 'testuser',
'email' => 'test@example.com',
]);
$this->assertDatabaseHas('users', [
'username' => 'testuser',
'email' => 'test@example.com',
]);
}
}
在終端機中執行:
php artisan test --filter=it_can_create_a_user
如果測試通過,恭喜你!我們的第一個單元測試成功了。
建立測試類別:
php artisan make:test Models/BankAccountTest --unit
撰寫測試:
/** @test */
public function it_can_create_a_bank_account()
{
$user = User::factory()->create();
$bankAccount = $user->bankAccounts()->create([
'account_name' => '薪資帳戶',
'account_number' => '12345678',
'bank_name' => '台灣銀行',
'balance' => 1000.00,
]);
$this->assertDatabaseHas('bank_accounts', [
'account_name' => '薪資帳戶',
'user_id' => $user->id,
]);
}
同樣地,建立 CategoryTest,撰寫測試。
建立 TransactionTest,撰寫測試。
我們可以測試模型之間的關聯是否正確。
/** @test */
public function a_user_can_have_multiple_bank_accounts()
{
$user = User::factory()->create();
$user->bankAccounts()->createMany([
['account_name' => '帳戶一', 'balance' => 500],
['account_name' => '帳戶二', 'balance' => 1000],
]);
$this->assertCount(2, $user->bankAccounts);
}
小小補充
如果你不想在方法名稱中加上 test_ 前綴,可以使用 /** @test */ 註解來替代,它的作用是告訴PHPUnit 這個方法是一個測試
好處:
• 語義化:這樣可以讓測試方法名稱更符合自然語言,例如 user_can_be_created,而不需要強制使用test_ 前綴。
• 可讀性高:去掉了不必要的 test_ 前綴,讓測試方法的命名更簡潔和易讀。
Laravel 的 Factory 讓我們可以輕鬆生成測試資料。
// database/factories/UserFactory.php
public function definition()
{
return [
'username' => $this->faker->userName,
'email' => $this->faker->unique()->safeEmail,
'password' => bcrypt('password'),
];
}
我們可以在測試中使用 Seeder 來填充資料庫。
// tests/TestCase.php
protected function setUp(): void
{
parent::setUp();
$this->seed();
}
剛開始寫單元測試時,可能會覺得多了一道工序,但長遠來看,真的省下不少時間。以下是我的一些心得:
單元測試還可以延伸更多的議題,但我們先保留著~
透過單元測試,我們不僅能確保程式碼的品質,更能提升自己的開發技能。希望大家能夠開始嘗試在專案中加入單元測試,享受其中的樂趣與成就感。